Martin Buck delivers "track" upload for Garmin fitness devices.
authorrobertl <robertl>
Fri, 26 Mar 2010 03:33:18 +0000 (03:33 +0000)
committerrobertl <robertl>
Fri, 26 Mar 2010 03:33:18 +0000 (03:33 +0000)
Changes:
* If tracks and waypoints should be uploaded to a device supporting course
 upload, combine them to courses and course points and upload them together
* Split track/waypoint preparation and upload into separate functions in
 garmin.c since both preparation functions are needed together for
 course+course point upload
* Merged GPS_A301_Get()/GPS_A302_Get, GPS_A301_Send()/GPS_A302_Send()
* Properly handle track segment markers for fitness devices consisting of 2
 consecutive invalid track points
* Calculate lap data totals when creating a course. Also create time stamps
 (currently using a hardcoded speed of 10km/h) for track points that lack
 them. This is required so that course points can refer to track points and
 identify them uniquely.
* Fixed course garbage collection to remove unused track points properly. So
 far, we compared the track index with the course index instead of the
 course track index which inadvertently removed tracks that were
 refereneced and left others that weren't referenced.
* Remove duplicate course points (same course index and track point time
 stamp) because the protocol spec requires these two values to be unique.
* Create course points from waypoints by mapping them to the nearest track
 point.
* Use xrealloc() consistently

garmin.c
jeeps/gpsapp.c
jeeps/gpsapp.h
jeeps/gpscom.c
jeeps/gpscom.h

index 2f5eea20a6a780fe7f2c85785b972a12ce15932f..20b9c74b022b6b6a30c59d1849bdf94069cbba00 100644 (file)
--- a/garmin.c
+++ b/garmin.c
@@ -33,6 +33,7 @@
 #define MYNAME "GARMIN" 
 static const char *portname;
 static short_handle mkshort_handle;
+static GPS_PWay *tx_waylist;
 static GPS_PWay *tx_routelist;
 static GPS_PWay *cur_tx_routelist_entry;
 static GPS_PTrack *tx_tracklist;
@@ -818,21 +819,19 @@ get_gc_info(waypoint *wpt)
        return "";
 }
 
-static void
-waypoint_write(void)
+static int
+waypoint_prepare(void)
 {
        int i;
-       int32 ret;
        int n = waypt_count();
        queue *elem, *tmp;
        extern queue waypt_head;
-       GPS_PWay *way;
        int icon;
 
-       way = xcalloc(n,sizeof(*way));
+       tx_waylist = xcalloc(n,sizeof(*tx_waylist));
 
        for (i = 0; i < n; i++) {
-               way[i] = sane_GPS_Way_New();
+               tx_waylist[i] = sane_GPS_Way_New();
        }
 
        i = 0;
@@ -859,18 +858,18 @@ waypoint_write(void)
                 * but rather a garmin "fixed length" buffer that's padded
                 * to the end with spaces.  So this is NOT (strlen+1).
                 */
-               memcpy(way[i]->ident, ident, strlen(ident));
+               memcpy(tx_waylist[i]->ident, ident, strlen(ident));
 
                if (global_opts.synthesize_shortnames) { 
                        xfree(ident);
                }
-               way[i]->ident[sizeof(way[i]->ident)-1] = 0;
+               tx_waylist[i]->ident[sizeof(tx_waylist[i]->ident)-1] = 0;
 
                // If we were explictly given a comment from GPX, use that. 
                //  This logic really is horrible and needs to be untangled.
                if (wpt->description && 
                    global_opts.smart_names && !wpt->gc_data->diff) {
-                       memcpy(way[i]->cmnt, wpt->description, strlen(wpt->description));
+                       memcpy(tx_waylist[i]->cmnt, wpt->description, strlen(wpt->description));
                } else {
                        if (global_opts.smart_names && 
                             wpt->gc_data->diff && wpt->gc_data->terr) {
@@ -881,16 +880,16 @@ xasprintf(&src, "%s %s", &wpt->shortname[2], src);
                                                get_gc_info(wpt),
                                                wpt->gc_data->diff, wpt->gc_data->terr, 
                                                src);
-                               memcpy(way[i]->cmnt, obuf, strlen(obuf));
+                               memcpy(tx_waylist[i]->cmnt, obuf, strlen(obuf));
                        } else  {
-                               memcpy(way[i]->cmnt, src, strlen(src));
+                               memcpy(tx_waylist[i]->cmnt, src, strlen(src));
                        }
                }
 
                
 
-               way[i]->lon = wpt->longitude;
-               way[i]->lat = wpt->latitude;
+               tx_waylist[i]->lon = wpt->longitude;
+               tx_waylist[i]->lat = wpt->latitude;
 
                if (deficon) {
                        icon = gt_find_icon_number_from_desc(deficon, PCX);
@@ -908,41 +907,52 @@ xasprintf(&src, "%s %s", &wpt->shortname[2], src);
                if (gps_waypt_type == 103) {
                        icon = d103_icon_number_from_symbol(wpt->icon_descr);
                }
-               way[i]->smbl = icon;
+               tx_waylist[i]->smbl = icon;
                if (wpt->altitude == unknown_alt) {
-                       way[i]->alt_is_unknown = 1;
-                       way[i]->alt = 0;
+                       tx_waylist[i]->alt_is_unknown = 1;
+                       tx_waylist[i]->alt = 0;
                } else {
-                       way[i]->alt = wpt->altitude;
+                       tx_waylist[i]->alt = wpt->altitude;
                }
                if (wpt->creation_time) {
-                       way[i]->time = wpt->creation_time;
-                       way[i]->time_populated = 1;
+                       tx_waylist[i]->time = wpt->creation_time;
+                       tx_waylist[i]->time_populated = 1;
                }
                if (category) {
-                       way[i]->category = 1 << (atoi(category) - 1);
+                       tx_waylist[i]->category = 1 << (atoi(category) - 1);
                }
                if (categorybits) {
-                       way[i]->category = categorybits;
+                       tx_waylist[i]->category = categorybits;
                }
 #if SOON
-               garmin_fs_garmin_before_write(wpt, way[i], gps_waypt_type);
+               garmin_fs_garmin_before_write(wpt, tx_waylist[i], gps_waypt_type);
 #endif
                i++;
        }
 
-       if ((ret = GPS_Command_Send_Waypoint(portname, way, n, waypt_write_cb)) < 0) {
+       return n;
+}
+
+static void
+waypoint_write(void)
+{
+       int i, n;
+       int32 ret;
+
+       n = waypoint_prepare();
+
+       if ((ret = GPS_Command_Send_Waypoint(portname, tx_waylist, n, waypt_write_cb)) < 0) {
                fatal(MYNAME ":communication error sending wayoints..\n");
        }
 
        for (i = 0; i < n; ++i) {
-               GPS_Way_Del(&way[i]);
+               GPS_Way_Del(&tx_waylist[i]);
        }
        if (global_opts.verbose_status) {
                fprintf(stdout, "\r\n");
                fflush(stdout);
        }
-       xfree(way);
+       xfree(tx_waylist);
 }
 
 static void
@@ -1063,8 +1073,8 @@ track_waypt_pr(const waypoint *wpt)
        cur_tx_tracklist_entry++;
 }
 
-static void
-track_write(void)
+static int
+track_prepare(void)
 {
        int i;
        int n = track_waypt_count() + track_count();
@@ -1077,6 +1087,16 @@ track_write(void)
        my_track_count = 0;
        track_disp_all(track_hdr_pr, route_noop, track_waypt_pr);
 
+       return n;
+}
+
+static void
+track_write(void)
+{
+       int i, n;
+
+       n = track_prepare();
+
        GPS_Command_Send_Track(portname, tx_tracklist, n);
 
        for (i = 0; i < n; i++) {
@@ -1085,6 +1105,28 @@ track_write(void)
        xfree(tx_tracklist);
 }
 
+static void
+course_write(void)
+{
+       int i, n_trk, n_wpt;
+
+       n_wpt = waypoint_prepare();
+       n_trk = track_prepare();
+
+       GPS_Command_Send_Track_As_Course(portname, tx_tracklist, n_trk,
+                                        tx_waylist, n_wpt);
+
+       for (i = 0; i < n_wpt; ++i) {
+               GPS_Way_Del(&tx_waylist[i]);
+       }
+       xfree(tx_waylist);
+
+       for (i = 0; i < n_trk; i++) {
+               GPS_Track_Del(&tx_tracklist[i]);
+       }
+       xfree(tx_tracklist);
+}
+
 static void
 data_write(void)
 {
@@ -1092,12 +1134,25 @@ data_write(void)
                return;
        }
 
-       if (global_opts.masked_objective & WPTDATAMASK)
-         waypoint_write();
+       /* If we have both trackpoints and waypoints and the device
+        * supports courses, combine them to a course. Otherwise,
+        * send tracks & waypoints separately.
+        */
+       if ((global_opts.masked_objective & WPTDATAMASK) &&
+           (global_opts.masked_objective & TRKDATAMASK) &&
+           gps_course_transfer != -1)
+       {
+         course_write();
+       }
+       else
+       {
+           if (global_opts.masked_objective & WPTDATAMASK)
+               waypoint_write();
+           if (global_opts.masked_objective & TRKDATAMASK)
+               track_write();
+       }
        if (global_opts.masked_objective & RTEDATAMASK)
          route_write();
-       if (global_opts.masked_objective & TRKDATAMASK)
-         track_write();
 }
 
 
index 860c5c4e2ada12aa6188dc2bddb782a6592b4b15..5525ea2fec0c2ebe23c0953552ff3ace02117d93 100644 (file)
@@ -104,7 +104,7 @@ static void   GPS_D501_Send(UC *data, GPS_PAlmanac alm);
 static void   GPS_D550_Send(UC *data, GPS_PAlmanac alm);
 static void   GPS_D551_Send(UC *data, GPS_PAlmanac alm);
 
-static UC Is_Trackpoint_Invalid(GPS_PTrack *trk);
+static UC Is_Trackpoint_Invalid(GPS_PTrack trk);
 
 
 int32  gps_save_id;
@@ -3634,14 +3634,15 @@ drain_run_cmd(gpsdevh *fd)
 
 /* @func GPS_A301_Get ******************************************************
 **
-** Get track data from GPS
+** Get track data from GPS (A301/A302)
 **
 ** @param [r] port [const char *] serial port
 ** @param [w] trk [GPS_PTrack **] track array
+** @param [r] protoid [int] protocol ID (301 or 302)
 **
 ** @return [int32] number of track entries
 ************************************************************************/
-int32 GPS_A301_Get(const char *port, GPS_PTrack **trk, pcb_fn cb)
+int32 GPS_A301_Get(const char *port, GPS_PTrack **trk, pcb_fn cb, int protoid)
 {
     static UC data[2];
     gpsdevh *fd;
@@ -3649,12 +3650,36 @@ int32 GPS_A301_Get(const char *port, GPS_PTrack **trk, pcb_fn cb)
     GPS_PPacket rec;
     int32 n;
     int32 i;
+    US Pid_Trk_Data, Pid_Trk_Hdr, Cmnd_Transfer_Trk;
+    int32 trk_type, trk_hdr_type;
 
     if(gps_trk_transfer == -1)
        return GPS_UNSUPPORTED;
 
+    /* A301 and A302 are similar except for all these protocol IDs */
+    switch (protoid)
+    {
+    case 301:
+       Pid_Trk_Data = LINK_ID[gps_link_type].Pid_Trk_Data;
+       Pid_Trk_Hdr = LINK_ID[gps_link_type].Pid_Trk_Hdr;
+       Cmnd_Transfer_Trk = COMMAND_ID[gps_device_command].Cmnd_Transfer_Trk;
+       trk_type = gps_trk_type;
+       trk_hdr_type = gps_trk_hdr_type;
+       break;
+    case 302:
+       Pid_Trk_Data = LINK_ID[gps_link_type].Pid_Course_Trk_Data;
+       Pid_Trk_Hdr = LINK_ID[gps_link_type].Pid_Course_Trk_Hdr;
+       Cmnd_Transfer_Trk = COMMAND_ID[gps_device_command].Cmnd_Transfer_Course_Tracks;
+       trk_type = gps_run_crs_trk_type;
+       trk_hdr_type = gps_run_crs_trk_hdr_type;
+       break;
+    default:
+       GPS_Error("A301_Get: Bad protocol ID %d", protoid);
+       return GPS_UNSUPPORTED;
+    }
+
     /* Only those GPS' with L001 can send track data */
-    if(!LINK_ID[gps_link_type].Pid_Trk_Data)
+    if(!Pid_Trk_Data)
     {
        GPS_Warning("A301 protocol unsupported");
        return GPS_UNSUPPORTED;
@@ -3663,15 +3688,14 @@ int32 GPS_A301_Get(const char *port, GPS_PTrack **trk, pcb_fn cb)
     if(!GPS_Device_On(port, &fd))
        return gps_errno;
 
-    if ((gps_trk_type == pD304) && gps_run_transfer != -1) {
+    if ((trk_type == pD304) && gps_run_transfer != -1) {
        drain_run_cmd(fd);
     }
 
     if(!(tra = GPS_Packet_New()) || !(rec = GPS_Packet_New()))
        return MEMORY_ERROR;
 
-    GPS_Util_Put_Short(data,
-                      COMMAND_ID[gps_device_command].Cmnd_Transfer_Trk);
+    GPS_Util_Put_Short(data, Cmnd_Transfer_Trk);
     GPS_Make_Packet(&tra, LINK_ID[gps_link_type].Pid_Command_Data,
                    data,2);
     if(!GPS_Write_Packet(fd,tra))
@@ -3702,9 +3726,9 @@ int32 GPS_A301_Get(const char *port, GPS_PTrack **trk, pcb_fn cb)
            return gps_errno;
        if(!GPS_Send_Ack(fd, &tra, &rec))
            return gps_errno;
-       if(rec->type == LINK_ID[gps_link_type].Pid_Trk_Hdr)
+       if(rec->type == Pid_Trk_Hdr)
        {
-           switch(gps_trk_hdr_type)
+           switch(trk_hdr_type)
            {
            case pD310:
            case pD312:
@@ -3721,7 +3745,7 @@ int32 GPS_A301_Get(const char *port, GPS_PTrack **trk, pcb_fn cb)
            continue;
        }
        
-       if(rec->type != LINK_ID[gps_link_type].Pid_Trk_Data)
+       if(rec->type != Pid_Trk_Data)
        {
            GPS_Error("A301_Get: Non-Pid_Trk_Data");
            return FRAMING_ERROR;
@@ -3729,7 +3753,7 @@ int32 GPS_A301_Get(const char *port, GPS_PTrack **trk, pcb_fn cb)
 
        (*trk)[i]->ishdr = 0;
        
-       switch(gps_trk_type)
+       switch(trk_type)
        {
        case pD300:
            GPS_D300b_Get(&((*trk)[i]),rec->data);
@@ -3743,13 +3767,28 @@ int32 GPS_A301_Get(const char *port, GPS_PTrack **trk, pcb_fn cb)
        case pD303:
        case pD304:
            GPS_D303b_Get(&((*trk)[i]),rec->data);
+           /* Fitness devices don't send track segment markers, so we have
+            * to create them ourselves. We do so at the beginning of the
+            * track or if the device signals a pause by sending two
+            * invalid points in a row.
+            */
+           if (i>0)
+           {
+               if ((*trk)[i-1]->ishdr ||
+                   (Is_Trackpoint_Invalid((*trk)[i-1]) &&
+                    Is_Trackpoint_Invalid((*trk)[i])))
+               {
+                   (*trk)[i]->tnew = 1;
+               }
+           }
            break;
        default:
            GPS_Error("A301_GET: Unknown track protocol");
            return PROTOCOL_ERROR;
        }
        /* Cheat and don't _really_ pass the trkpt back */
-       cb(n, NULL);
+       if (cb)
+           cb(n, NULL);
     }
 
     if(!GPS_Packet_Read(fd, &rec))
@@ -3778,165 +3817,6 @@ int32 GPS_A301_Get(const char *port, GPS_PTrack **trk, pcb_fn cb)
 }
 
 
-/* @func GPS_A302_Get ******************************************************
-**
-** Get course track data from GPS with protocol A302
-**
-** @param [r] port [const char *] serial port
-** @param [w] trk [GPS_PTrack **] track array
-**
-** @return [int32] number of track entries
-************************************************************************/
-/* FIXME: Merge with GPS_A301_Get()? */
-int32 GPS_A302_Get(const char *port, GPS_PTrack **trk, pcb_fn cb)
-{
-    static UC data[2];
-    gpsdevh *fd;
-    GPS_PPacket tra;
-    GPS_PPacket rec;
-    int32 n;
-    int32 i;
-
-    if(gps_trk_transfer == -1)
-       return GPS_UNSUPPORTED;
-
-    /* Only those GPS' with L001 can send track data */
-    if  (!LINK_ID[gps_link_type].Pid_Course_Trk_Data)
-    {
-       GPS_Warning("Course track data unsupported");
-       return GPS_UNSUPPORTED;
-    }
-
-    if(!GPS_Device_On(port, &fd))
-       return gps_errno;
-
-    if(!(tra = GPS_Packet_New()) || !(rec = GPS_Packet_New()))
-       return MEMORY_ERROR;
-
-    GPS_Util_Put_Short(data,
-                    COMMAND_ID[gps_device_command].Cmnd_Transfer_Course_Tracks);
-    GPS_Make_Packet(&tra, LINK_ID[gps_link_type].Pid_Command_Data,
-                   data,2);
-    if(!GPS_Write_Packet(fd,tra))
-       return gps_errno;
-    if(!GPS_Get_Ack(fd, &tra, &rec))
-       return gps_errno;
-    if(!GPS_Packet_Read(fd, &rec))
-       return gps_errno;
-    if(!GPS_Send_Ack(fd, &tra, &rec))
-       return gps_errno;
-
-
-    n = GPS_Util_Get_Short(rec->data);
-
-    if(n)
-       if(!((*trk)=(GPS_PTrack *)malloc(n*sizeof(GPS_PTrack))))
-       {
-           GPS_Error("A302b_Get: Insufficient memory");
-           return MEMORY_ERROR;
-       }
-    for(i=0;i<n;++i)
-       if(!((*trk)[i]=GPS_Track_New()))
-           return MEMORY_ERROR;
-
-    for(i=0;i<n;++i)
-    {
-       if(!GPS_Packet_Read(fd, &rec))
-           return gps_errno;
-       if(!GPS_Send_Ack(fd, &tra, &rec))
-           return gps_errno;
-       if (rec->type == LINK_ID[gps_link_type].Pid_Course_Trk_Hdr)
-       {
-           switch(gps_run_crs_trk_hdr_type)
-           {
-           case pD310:
-           case pD312:
-               GPS_D310_Get(&((*trk)[i]),rec->data);
-               break;
-           case pD311:
-               GPS_D311_Get(&((*trk)[i]),rec->data);
-               break;
-           default:
-               GPS_Error("A302b_Get: Unknown track header data");
-               return PROTOCOL_ERROR;
-           }
-           (*trk)[i]->ishdr = 1;
-           continue;
-       }
-
-       if  (rec->type != LINK_ID[gps_link_type].Pid_Course_Trk_Data)
-       {
-           GPS_Error("A302b_Get: Non-Pid_Course_Trk_Data");
-           return FRAMING_ERROR;
-       }
-
-       (*trk)[i]->ishdr = 0;
-
-       switch(gps_run_crs_trk_type)
-       {
-       case pD300:
-           GPS_D300b_Get(&((*trk)[i]),rec->data);
-           break;
-       case pD301:
-           GPS_D301b_Get(&((*trk)[i]),rec->data);
-           break;
-       case pD302:
-           GPS_D302b_Get(&((*trk)[i]),rec->data);
-           break;
-       case pD303:
-       case pD304:
-           GPS_D303b_Get(&((*trk)[i]),rec->data);
-           /* Fitness devices don't send track segment markers, so we have
-            * to create them ourselves. We do so at the beginning of the
-            * track or if the device signals a pause by sending two
-            * invalid points in a row.
-            */
-            if (i>0)
-            {
-                if ((*trk)[i-1]->ishdr ||
-                    (Is_Trackpoint_Invalid(&((*trk)[i-1])) &&
-                     Is_Trackpoint_Invalid(&((*trk)[i]))))
-                {
-                    (*trk)[i]->tnew = 1;
-                }
-            }
-           break;
-       default:
-           GPS_Error("A301_GET: Unknown track protocol");
-           return PROTOCOL_ERROR;
-       }
-       /* Cheat and don't _really_ pass the trkpt back */
-       if (cb) {
-           cb(n, NULL);
-        }
-    }
-
-    if(!GPS_Packet_Read(fd, &rec))
-       return gps_errno;
-    if(!GPS_Send_Ack(fd, &tra, &rec))
-       return gps_errno;
-    if(rec->type != LINK_ID[gps_link_type].Pid_Xfer_Cmplt)
-    {
-       GPS_Error("A302b_Get: Error transferring course tracks");
-       return FRAMING_ERROR;
-    }
-
-    if(i != n)
-    {
-       GPS_Error("A302b_Get: Course track entry number mismatch");
-       return FRAMING_ERROR;
-    }
-
-    GPS_Packet_Del(&tra);
-    GPS_Packet_Del(&rec);
-
-    if(!GPS_Device_Off(fd))
-       return gps_errno;
-
-    return n;
-}
-
-
 /* @func GPS_A300_Send **************************************************
 **
 ** Send track log to GPS
@@ -4032,35 +3912,100 @@ int32 GPS_A300_Send(const char *port, GPS_PTrack *trk, int32 n)
 
 /* @func GPS_A301_Send **************************************************
 **
-** Send track log to GPS
+** Send track log to GPS (A301/A302). Note that in case of A302, track
+** log transfer is part of the course transfer sequence, so we must not
+** call GPS_Device_On/Off() but expect to get a usable gpsdevh from our
+** caller.
 **
 ** @param [r] port [const char *] serial port
 ** @param [r] trk [GPS_PTrack *] track array
 ** @param [r] n [int32] number of track entries
+** @param [r] protoid [int] protocol ID (301 or 302)
+** @param [r] fd [gpsdevh *] pointer to communication port (for A302 only)
 **
 ** @return [int32] success
 ************************************************************************/
-int32 GPS_A301_Send(const char *port, GPS_PTrack *trk, int32 n)
+int32 GPS_A301_Send(const char *port, GPS_PTrack *trk, int32 n, int protoid,
+                    gpsdevh *fd)
 {
     UC data[GPS_ARB_LEN];
-    gpsdevh *fd;
     GPS_PPacket tra;
     GPS_PPacket rec;
-    int32 i;
+    int32 i, j;
     int32 len;
     US  method;
+    US Pid_Trk_Data, Pid_Trk_Hdr, Cmnd_Transfer_Trk;
+    int32 trk_type, trk_hdr_type;
 
     if(gps_trk_transfer == -1)
        return GPS_UNSUPPORTED;
 
+    /* A301 and A302 are similar except for all these protocol IDs */
+    switch (protoid)
+    {
+    case 301:
+       Pid_Trk_Data = LINK_ID[gps_link_type].Pid_Trk_Data;
+       Pid_Trk_Hdr = LINK_ID[gps_link_type].Pid_Trk_Hdr;
+       Cmnd_Transfer_Trk = COMMAND_ID[gps_device_command].Cmnd_Transfer_Trk;
+       trk_type = gps_trk_type;
+       trk_hdr_type = gps_trk_hdr_type;
+       break;
+    case 302:
+       Pid_Trk_Data = LINK_ID[gps_link_type].Pid_Course_Trk_Data;
+       Pid_Trk_Hdr = LINK_ID[gps_link_type].Pid_Course_Trk_Hdr;
+       Cmnd_Transfer_Trk = COMMAND_ID[gps_device_command].Cmnd_Transfer_Course_Tracks;
+       trk_type = gps_run_crs_trk_type;
+       trk_hdr_type = gps_run_crs_trk_hdr_type;
+       break;
+    default:
+       GPS_Error("A301_Send: Bad protocol ID %d", protoid);
+       return GPS_UNSUPPORTED;
+    }
+    
     /* Only those GPS' with L001 can send track data */
-    if(!LINK_ID[gps_link_type].Pid_Trk_Data)
+    if(!Pid_Trk_Data)
     {
        GPS_Warning("A301 protocol unsupported");
        return GPS_UNSUPPORTED;
     }
 
-    if(!GPS_Device_On(port, &fd))
+    /* D303/304 marks track segments with two consecutive invalid track
+     * points. Create them unless we're at the beginning of a track or there
+     * are already invalid track points (because the track was downloaded
+     * using D303/304). This needs to be done here because it will change
+     * the number of track points.
+     */
+    if (trk_type == pD303 || trk_type == pD304)
+    {
+       for(i=0;i<n;++i)
+       {
+           if (trk[i]->tnew && i>0 && !trk[i]->ishdr && !trk[i-1]->ishdr)
+           {
+               /* Create in reverse order to avoid having to readjust the
+                * index after inserting the first point.
+                */
+               for (j=i; j>=i-1; j--)
+               {
+                   if (!Is_Trackpoint_Invalid(trk[j]))
+                   {
+                       GPS_PTrack trkpt = GPS_Track_New();
+                       *trkpt = *(trk[j]);
+                       trkpt->no_latlon = 1;
+                       trkpt->alt = 1e25;
+                       trkpt->distance = 1e25;
+                       trkpt->heartrate = 0;
+                       trkpt->cadence = 0xff;
+                       trk = xrealloc(trk, (n+1) * sizeof(GPS_PTrack));
+                       memmove(&trk[i+1], &trk[i], (n-i) * sizeof(GPS_PTrack));
+                       n++;
+                       trk[i] = trkpt;
+                   }
+               }
+           }
+       }
+    }
+
+    if(protoid != 302 && !GPS_Device_On(port, &fd))
        return gps_errno;
 
     if(!(tra = GPS_Packet_New()) || !(rec = GPS_Packet_New()))
@@ -4082,9 +4027,9 @@ int32 GPS_A301_Send(const char *port, GPS_PTrack *trk, int32 n)
     {
        if(trk[i]->ishdr)
        {
-           method = LINK_ID[gps_link_type].Pid_Trk_Hdr;
+           method = Pid_Trk_Hdr;
 
-           switch(gps_trk_hdr_type)
+           switch(trk_hdr_type)
            {
            case pD310:
            case pD312:
@@ -4094,15 +4039,15 @@ int32 GPS_A301_Send(const char *port, GPS_PTrack *trk, int32 n)
                GPS_D311_Send(data,trk[i],&len);
                break;
            default:
-               GPS_Error("A301_Send: Unknown track protocol %d", gps_trk_hdr_type);
+               GPS_Error("A301_Send: Unknown track protocol %d", trk_hdr_type);
                return PROTOCOL_ERROR;
            }
        }
        else
        {
-           method = LINK_ID[gps_link_type].Pid_Trk_Data;
+           method = Pid_Trk_Data;
 
-           switch(gps_trk_type)
+           switch(trk_type)
            {
            case pD300:
                GPS_D300_Send(data,trk[i],&len);
@@ -4113,18 +4058,15 @@ int32 GPS_A301_Send(const char *port, GPS_PTrack *trk, int32 n)
            case pD302:
                GPS_D301_Send(data,trk[i],&len,302);
                break;
-           case pD303:
-               GPS_D303_Send(data,trk[i],&len,303);
-               break;
+           case pD303:
            case pD304:
-               GPS_D303_Send(data,trk[i],&len,304);
+               GPS_D303_Send(data,trk[i],&len,(trk_type==pD303) ? 303 : 304);
                break;
            default:
                GPS_Error("A301_Send: Unknown track protocol");
                return PROTOCOL_ERROR;
            }
        }
-       
 
        GPS_Make_Packet(&tra, method, data, len);
 
@@ -4137,9 +4079,8 @@ int32 GPS_A301_Send(const char *port, GPS_PTrack *trk, int32 n)
            return FRAMING_ERROR;
        }
     }
-       
 
-    GPS_Util_Put_Short(data,COMMAND_ID[gps_device_command].Cmnd_Transfer_Trk);
+    GPS_Util_Put_Short(data, Cmnd_Transfer_Trk);
     GPS_Make_Packet(&tra, LINK_ID[gps_link_type].Pid_Xfer_Cmplt,
                    data,2);
     if(!GPS_Write_Packet(fd,tra))
@@ -4153,143 +4094,13 @@ int32 GPS_A301_Send(const char *port, GPS_PTrack *trk, int32 n)
     GPS_Packet_Del(&tra);
     GPS_Packet_Del(&rec);
 
-    if(!GPS_Device_Off(fd))
+    if(protoid != 302 && !GPS_Device_Off(fd))
        return gps_errno;
 
     return 1;
 }
 
 
-/* @func GPS_A302_Send **************************************************
-**
-** Send Course-track log to GPS
-**
-** Note that different to other GPS_Axxx_Send functions, the device
-** communication is not initialized/ended within the function, since
-** this packet transfer is only part of a series of transfers to the
-** device. Communication init/end has to be handled by the caller.
-**
-** @param [r] port [const char *] serial port
-** @param [r] trk [GPS_PTrack *] track array
-** @param [r] n [int32] number of track entries
-** @param [r] fd [gpsdevh *] pointer to the communication port
-**
-** @return [int32] success
-************************************************************************/
-/* FIXME: Merge with GPS_A301_Send()? */
-int32 GPS_A302_Send(const char *port,
-                     GPS_PTrack *trk,
-                     int32 n,
-                     gpsdevh *fd)
-{
-    UC data[GPS_ARB_LEN];
-    GPS_PPacket tra;
-    GPS_PPacket rec;
-    int32 i;
-    int32 len;
-    US  method;
-
-    if(gps_trk_transfer == -1)
-       return GPS_UNSUPPORTED;
-
-    /* Only those GPS' with L001 can send track data */
-    if(!LINK_ID[gps_link_type].Pid_Course_Trk_Data)
-    {
-       GPS_Warning("A302b: course-track protocol unsupported");
-       return GPS_UNSUPPORTED;
-    }
-
-    if(!(tra = GPS_Packet_New()) || !(rec = GPS_Packet_New()))
-       return MEMORY_ERROR;
-
-    GPS_Util_Put_Short(data,(US) n);
-    GPS_Make_Packet(&tra, LINK_ID[gps_link_type].Pid_Records,
-                   data,2);
-    if(!GPS_Write_Packet(fd,tra))
-       return gps_errno;
-    if(!GPS_Get_Ack(fd, &tra, &rec))
-    {
-       GPS_Error("A302_Send: Course-track start data not acknowledged");
-       return FRAMING_ERROR;
-    }
-
-    for(i=0;i<n;++i)
-    {
-       if(trk[i]->ishdr)
-       {
-           method = LINK_ID[gps_link_type].Pid_Course_Trk_Hdr;
-
-           switch(gps_run_crs_trk_hdr_type)
-           {
-           case pD310:
-           case pD312:
-               GPS_D310_Send(data,trk[i],&len);
-               break;
-           case pD311:
-               GPS_D311_Send(data,trk[i],&len);
-               break;
-           default:
-               GPS_Error("A302_Send: Unknown track header type");
-               return PROTOCOL_ERROR;
-           }
-       }
-       else
-       {
-           method = LINK_ID[gps_link_type].Pid_Course_Trk_Data;
-
-           switch(gps_run_crs_trk_type)
-           {
-           case pD300:
-               GPS_D300_Send(data,trk[i],&len);
-               break;
-           case pD301:
-               GPS_D301_Send(data,trk[i],&len, 301);
-               break;
-           case pD302:
-               GPS_D301_Send(data,trk[i],&len, 302);
-               break;
-           case pD303:
-               GPS_D303_Send(data,trk[i],&len,303);
-               break;
-           case pD304:
-               GPS_D303_Send(data,trk[i],&len,304);
-               break;
-           default:
-               GPS_Error("A302_Send: Unknown track protocol");
-               return PROTOCOL_ERROR;
-           }
-       }
-
-       GPS_Make_Packet(&tra, method, data,(US) len);
-
-       if(!GPS_Write_Packet(fd,tra))
-           return gps_errno;
-
-       if(!GPS_Get_Ack(fd, &tra, &rec))
-       {
-          GPS_Error("A302_Send: Course-track packet not acknowledged");
-          return FRAMING_ERROR;
-       }
-    }
-
-    GPS_Util_Put_Short(data,COMMAND_ID[gps_device_command].Cmnd_Transfer_Course_Tracks);
-    GPS_Make_Packet(&tra, LINK_ID[gps_link_type].Pid_Xfer_Cmplt,
-                   data,2);
-    if(!GPS_Write_Packet(fd,tra))
-       return gps_errno;
-    if(!GPS_Get_Ack(fd, &tra, &rec))
-    {
-       GPS_Error("A302_Send: Course-track complete data not acknowledged");
-       return FRAMING_ERROR;
-    }
-
-    GPS_Packet_Del(&tra);
-    GPS_Packet_Del(&rec);
-
-    return 1;
-}
-
-
 /* @func GPS_D300_Get ******************************************************
 **
 ** Get track data
@@ -4685,7 +4496,7 @@ void GPS_D303_Send(UC *data, GPS_PTrack trk, int32 *len, int protoid)
     p+=sizeof(float);
 
     if (protoid == 304) {
-       GPS_Util_Put_Float(p, 1.0e25f);
+       GPS_Util_Put_Float(p, 1.0e25f); /* Distance, not supported for now */
        p+=sizeof(float);
     }
 
@@ -4693,10 +4504,10 @@ void GPS_D303_Send(UC *data, GPS_PTrack trk, int32 *len, int protoid)
     p+=sizeof(char);
 
     if (protoid == 304) {
-       *p = trk->cadence;
+       *p = trk->cadence > 0 ? trk->cadence : 0xff;
        p+=sizeof(char);
 
-       *p = trk->cadence > 0 ? trk->cadence : 0xff;
+       *p = 0; /* Wheel sensor present, not supported for now */
        p+=sizeof(char);
     }
 
@@ -4807,10 +4618,13 @@ static void GPS_A300_Encode(UC *s, GPS_PTrack trk)
 
     p=s;
 
-    GPS_Util_Put_Int(p,GPS_Math_Deg_To_Semi(trk->lat));
+    /* Note: lat/lon == 0x7fffffff is only valid for D303/D304, but our
+     * caller shouldn't set no_latlon unless one of these protocols actually
+     * is in use */
+    GPS_Util_Put_Int(p,trk->no_latlon ? 0x7fffffff : GPS_Math_Deg_To_Semi(trk->lat));
     p+=sizeof(int32);
 
-    GPS_Util_Put_Int(p,GPS_Math_Deg_To_Semi(trk->lon));
+    GPS_Util_Put_Int(p,trk->no_latlon ? 0x7fffffff : GPS_Math_Deg_To_Semi(trk->lon));
     p+=sizeof(int32);
 
     GPS_Util_Put_Uint(p,GPS_Math_Utime_To_Gtime(trk->Time));
@@ -7515,13 +7329,13 @@ Get_Pkt_Type(US p, US d0, const char **xinfo)
 **
 ** @return [UC] 1 if the trackpoint is invalid
 ************************************************************************/
-static UC Is_Trackpoint_Invalid(GPS_PTrack *trk)
+static UC Is_Trackpoint_Invalid(GPS_PTrack trk)
 {
     /* FIXME: We should have more *_is_unknown fields instead of
      * checking for special values here (e.g. cadence = 0 would be
      * perfectly valid, but GPS_D303b_Get() chose to use it to mark
      * nonexistent cadence data.
      */
-    return (*trk)->no_latlon && (*trk)->distance > 1e24 &&
-           !(*trk)->heartrate && !(*trk)->cadence;
+    return trk->no_latlon && trk->distance > 1e24 &&
+           !trk->heartrate && !trk->cadence;
 }
index 300bddf3fc674f6a8256a3fff55f3d0086d87d3a..b835cfbd592be944a63404ee6e33fd700352df0f 100644 (file)
@@ -21,11 +21,9 @@ int32  GPS_A200_Send(const char *port, GPS_PWay *way, int32 n);
 int32  GPS_A201_Send(const char *port, GPS_PWay *way, int32 n);
 
 int32  GPS_A300_Get(const char *port, GPS_PTrack **trk, pcb_fn cb);
-int32  GPS_A301_Get(const char *port, GPS_PTrack **trk, pcb_fn cb);
-int32  GPS_A302_Get(const char *port, GPS_PTrack **trk, pcb_fn cb);
+int32  GPS_A301_Get(const char *port, GPS_PTrack **trk, pcb_fn cb, int protoid);
 int32  GPS_A300_Send(const char *port, GPS_PTrack *trk, int32 n);
-int32  GPS_A301_Send(const char *port, GPS_PTrack *trk, int32 n);
-int32  GPS_A302_Send(const char *port, GPS_PTrack *trk, int32 n,
+int32  GPS_A301_Send(const char *port, GPS_PTrack *trk, int32 n, int protoid,
                     gpsdevh *fd);
 
 int32  GPS_D300_Get(GPS_PTrack *trk, int32 entries, gpsdevh *h);
index 302c39d709e37760c1c5c9d8ef9474093305c340..77f68a8b1b880d0fc5b549204d94410e80a6d606 100644 (file)
@@ -26,6 +26,7 @@
 ********************************************************************/
 #include "gps.h"
 #include <stdio.h>
+#include <float.h>
 
 
 /* @func GPS_Command_Off ***********************************************
@@ -235,7 +236,7 @@ int32 GPS_Command_Get_Track(const char *port, GPS_PTrack **trk, pcb_fn cb)
        break;
     case pA301:
     case pA302:
-       ret = GPS_A301_Get(port,trk,cb);
+       ret = GPS_A301_Get(port,trk,cb,301);
        break;
     default:
        GPS_Error("Get_Track: Unknown track protocol %d\n", gps_trk_transfer);
@@ -271,13 +272,13 @@ int32 GPS_Command_Send_Track(const char *port, GPS_PTrack *trk, int32 n)
        ret = GPS_A300_Send(port, trk, n);
        break;
     case pA301:
-       ret = GPS_A301_Send(port, trk, n);
+       ret = GPS_A301_Send(port, trk, n, 301, NULL);
        break;
     case pA302:
        /* Units with A302 don't support track upload, so we convert the
         * track to a course on the fly and send that instead
         */
-       ret = GPS_Command_Send_Track_As_Course(port, trk, n);
+       ret = GPS_Command_Send_Track_As_Course(port, trk, n, NULL, 0);
        break;
     default:
        GPS_Error("Send_Track: Unknown track protocol %d.", gps_trk_transfer);
@@ -740,7 +741,7 @@ int32  GPS_Command_Get_Course
                             gps_trk_transfer);
            break;
         case pA302:
-           *n_trk = GPS_A302_Get(port,trk,cb);
+           *n_trk = GPS_A301_Get(port,trk,cb,302);
            break;
         default:
            GPS_Error("Get_Course: Unknown course track protocol %d\n",
@@ -768,10 +769,6 @@ int32  GPS_Command_Get_Course
 ** includes sending all course laps, course tracks and course points
 ** to the device.
 **
-** Data integrity is being checked: only those course laps, tracks and
-** points are sent to the device which belong to a course in the course
-** array. Otherwise, those data will be silently dropped.
-**
 ** @param [r] port [const char *] serial port
 ** @param [w] crs [GPS_PCourse **] course array
 ** @param [w] clp [GPS_PCourse_Lap *] course lap array
@@ -841,7 +838,7 @@ int32  GPS_Command_Send_Course
                             gps_trk_transfer);
            break;
         case pA302:
-           ret_trk = GPS_A302_Send(port,trk,n_trk,fd);
+           ret_trk = GPS_A301_Send(port,trk,n_trk,302,fd);
            break;
         default:
            GPS_Error("Send_Course: Unknown course track protocol %d\n",
@@ -867,6 +864,15 @@ int32  GPS_Command_Send_Course
 }
 
 
+/* @funcstatic Unique_Course_Index *************************************
+**
+** Choose a course index that's not yet used by another course.
+**
+** @param [r] crs [GPS_PCourse **] course array
+** @param [r] n_crs [int32] number of course entries
+**
+** @return [uint32] course index
+************************************************************************/
 static uint32 Unique_Course_Index(GPS_PCourse *crs, int n_crs)
 {
     uint32 idx;
@@ -874,15 +880,25 @@ static uint32 Unique_Course_Index(GPS_PCourse *crs, int n_crs)
 
     for (idx=0; ; idx++)
     {
-       for (i=0; i<n_crs; i++)
-           if (crs[i]->index==idx)
-               break; /* Already have this index */
-       if (i>=n_crs)
-           return idx; /* Found unused index */
+        for (i=0; i<n_crs; i++)
+            if (crs[i]->index==idx)
+                break; /* Already have this index */
+        if (i>=n_crs)
+            return idx; /* Found unused index */
     }
 }
 
 
+/* @funcstatic Unique_Track_Index ***************************************
+**
+** Choose a track index that's not yet used by another track referenced
+** by the courses.
+**
+** @param [r] crs [GPS_PCourse **] course array
+** @param [r] n_crs [int32] number of course entries
+**
+** @return [uint32] track index
+************************************************************************/
 static uint32 Unique_Track_Index(GPS_PCourse *crs, int n_crs)
 {
     uint32 idx;
@@ -890,99 +906,220 @@ static uint32 Unique_Track_Index(GPS_PCourse *crs, int n_crs)
 
     for (idx=0; ; idx++)
     {
-       for (i=0; i<n_crs; i++)
-           if (crs[i]->track_index==idx)
-               break; /* Already have this index */
-       if (i>=n_crs)
-           return idx; /* Found unused index */
+        for (i=0; i<n_crs; i++)
+            if (crs[i]->track_index==idx)
+                break; /* Already have this index */
+        if (i>=n_crs)
+            return idx; /* Found unused index */
     }
 }
 
 
+/* @funcstatic Calculate_Course_Lap_Data *******************************
+**
+** Calculate lap data totals from individual track points. Also
+** generates time stamps for track points if they don't have
+** time stamps yet (using an arbitrary speed of 10 km/h which is
+** currently hardcoded. This is required so that couse points can
+** refer to track points and identify them uniquely.
+**
+** @param [w] clp [GPS_PCourse_Lap] course lap to be calculated
+** @param [r] ctk [GPS_PTrack *] track array to calculate lap from
+** @param [r] ctk_start [int] start index of lap in track array
+** @param [r] ctk_end [int] end index of lap in track array
+**
+** @return [void]
+************************************************************************/
+static void
+Calculate_Course_Lap_Data(GPS_PCourse_Lap clp, GPS_PTrack *ctk,
+                          int ctk_start, int ctk_end)
+{
+    int i;
+    double heartrate_sum = 0, cadence_sum = 0;
+    int heartrate_sum_time = 0, cadence_sum_time = 0;
+    double time_synth_speed = 10.0 * 1000 / 3600; /* speed in m/s */
+
+    if (ctk_start && ctk_end && !ctk[ctk_start]->Time)
+       ctk[ctk_start]->Time = GPS_Time_Now();
+    else
+       time_synth_speed = 0;
+
+    clp->total_dist = 0;
+    clp->avg_heart_rate = 0;
+    clp->max_heart_rate = 0;
+    clp->intensity = 0;
+    clp->avg_cadence = 0xff;
+    for (i=ctk_start; i <= ctk_end; i++)
+    {
+       if (ctk[i]->heartrate && ctk[i]->heartrate > clp->max_heart_rate)
+           clp->max_heart_rate = ctk[i]->heartrate;
+       if (i < ctk_end)
+       {
+           double dist = 0;
+           int seg_time;
+
+           if (!ctk[i]->no_latlon && !ctk[i+1]->no_latlon)
+               dist = gcgeodist(ctk[i]->lat, ctk[i]->lon,
+                                ctk[i+1]->lat, ctk[i+1]->lon);
+           clp->total_dist += dist;
+
+           if (time_synth_speed)
+               ctk[i+1]->Time = ctk[i]->Time + (dist / time_synth_speed + 0.5);
+
+           seg_time = ctk[i+1]->Time - ctk[i]->Time;
+
+           if (ctk[i]->heartrate)
+           {
+               heartrate_sum += ctk[i]->heartrate * seg_time;
+               heartrate_sum_time += seg_time;
+           }
+           if (ctk[i]->cadence)
+           {
+               cadence_sum += ctk[i]->cadence * seg_time;
+               cadence_sum_time += seg_time;
+           }
+       }
+    }
+
+    clp->total_time = 0;
+    clp->begin_lat = 0x7fffffff;
+    clp->begin_lon = 0x7fffffff;
+    clp->end_lat = 0x7fffffff;
+    clp->end_lon = 0x7fffffff;
+    if (ctk_start && ctk_end)
+    {
+       clp->total_time = (ctk[ctk_end]->Time - ctk[ctk_start]->Time) * 100;
+       if (!ctk[ctk_start]->no_latlon && !ctk[ctk_end]->no_latlon)
+       {
+           clp->begin_lat = ctk[ctk_start]->lat;
+           clp->begin_lon = ctk[ctk_start]->lon;
+           clp->end_lat = ctk[ctk_end]->lat;
+           clp->end_lon = ctk[ctk_end]->lon;
+       }
+    }
+    if (heartrate_sum_time)
+       clp->avg_heart_rate = heartrate_sum / heartrate_sum_time;
+    if (cadence_sum_time)
+       clp->avg_cadence = cadence_sum / cadence_sum_time;
+}
+
+
+/* @funcstatic Course_Garbage_Collect **********************************
+**
+** Remove duplicate courses, then remove unreferenced laps, tracks and
+** course points from arrays.
+**
+** @param [w] crs [GPS_PCourse *] course array
+** @param [w] n_crs [int *] number of course entries
+** @param [w] clp [GPS_PCourse_Lap *] course lap array
+** @param [w] n_clp [int *] number of lap entries
+** @param [w] ctk [GPS_PTrack *] track array
+** @param [w] n_ctk [int *] number of track entries
+** @param [w] cpt [GPS_PCourse_Point *] course point array
+** @param [w] n_cpt [int *] number of course point entries
+**
+** @return [void]
+************************************************************************/
 static void
 Course_Garbage_Collect(GPS_PCourse *crs, int *n_crs,
                        GPS_PCourse_Lap *clp, int *n_clp,
                        GPS_PTrack *ctk, int *n_ctk,
-                       GPS_PCourse_Point *cpt, int *n_cpt) {
+                       GPS_PCourse_Point *cpt, int *n_cpt)
+{
     int i, j;
 
-    /* Remove courses with duplicate names, keeping newest */
+    /* Remove courses with duplicate names, keeping newest.
+     * This is actually pretty important: Sending two courses with the same
+     * name to the device will result in internal data corruption on the
+     * device (e.g. "inventing" laps with weird course IDs that nobody ever
+     * transferred to it, that change with every upload of unrelated data
+     * and that can't be deleted except with a master reset by holding the
+     * Mode button during power on).
+     */
 restart_courses:
     for (i=*n_crs-1; i>0; i--)
     {
-       for (j=i-1; j>=0; j--)
-       {
-           if (!strcmp(crs[i]->course_name, crs[j]->course_name))
-           {
-               /* Remove course */
-               GPS_Course_Del(&crs[j]);
-               memmove(&crs[j], &crs[j+1], (*n_crs-j-1)*sizeof(*crs));
-               (*n_crs)--;
-               goto restart_courses;
-           }
-       }
+        for (j=i-1; j>=0; j--)
+        {
+            if (!strcmp(crs[i]->course_name, crs[j]->course_name))
+            {
+                /* Remove course */
+                GPS_Course_Del(&crs[j]);
+                memmove(&crs[j], &crs[j+1], (*n_crs-j-1)*sizeof(*crs));
+                (*n_crs)--;
+                goto restart_courses;
+            }
+        }
     }
 
   /* Remove unreferenced laps */
 restart_laps:
-  for (i=0; i<*n_clp; i++)
-  {
-    for (j=0; j<*n_crs; j++)
-      if (crs[j]->index == clp[i]->course_index)
-        break;
-    if (j>=*n_crs)
+    for (i=0; i<*n_clp; i++)
     {
-      /* Remove lap */
-      GPS_Course_Lap_Del(&clp[i]);
-      memmove(&clp[i], &clp[i+1], (*n_clp-i-1)*sizeof(*clp));
-      (*n_clp)--;
-      goto restart_laps;
+        for (j=0; j<*n_crs; j++)
+            if (crs[j]->index == clp[i]->course_index)
+                break;
+        if (j>=*n_crs)
+        {
+            /* Remove lap */
+            GPS_Course_Lap_Del(&clp[i]);
+            memmove(&clp[i], &clp[i+1], (*n_clp-i-1)*sizeof(*clp));
+            (*n_clp)--;
+            goto restart_laps;
+        }
     }
-  }
 
-  /* Remove unreferenced tracks */
+    /* Remove unreferenced tracks */
 restart_tracks:
-  for (i=0; i<*n_ctk; i++)
-  {
-    uint32 trk_idx;
-
-    if (!ctk[i]->ishdr)
-      continue;
-    trk_idx = strtoul(ctk[i]->trk_ident, NULL, 0);
-    for (j=0; j<*n_crs; j++)
-      if (crs[j]->index == trk_idx)
-        break;
-    if (j>=*n_crs)
+    for (i=0; i<*n_ctk; i++)
     {
-      /* Remove track */
-      for (j=i; j<*n_ctk; j++)
-      {
-        if (j!=i && ctk[j]->ishdr)
-          break;
-        GPS_Track_Del(&ctk[j]);
-      }
-      memmove(&ctk[i], &ctk[j], (*n_ctk-j)*sizeof(*ctk));
-      *(n_ctk) -= j-i;
-      goto restart_tracks;
+        uint32 trk_idx;
+
+        if (!ctk[i]->ishdr)
+            continue;
+        trk_idx = strtoul(ctk[i]->trk_ident, NULL, 0);
+        for (j=0; j<*n_crs; j++)
+            if (crs[j]->track_index == trk_idx)
+                break;
+        if (j>=*n_crs)
+        {
+            /* Remove track */
+            for (j=i; j<*n_ctk; j++)
+            {
+                if (j!=i && ctk[j]->ishdr)
+                    break;
+                GPS_Track_Del(&ctk[j]);
+            }
+            memmove(&ctk[i], &ctk[j], (*n_ctk-j)*sizeof(*ctk));
+            *(n_ctk) -= j-i;
+            goto restart_tracks;
+        }
     }
-  }
 
-  /* Remove unreferenced course points */
+    /* Remove unreferenced/duplicate course points */
 restart_course_points:
-  for (i=0; i<*n_cpt; i++)
-  {
-    for (j=0; j<*n_crs; j++)
-      if (crs[j]->index == cpt[i]->course_index)
-        break;
-    if (j>=*n_crs)
+    for (i=0; i<*n_cpt; i++)
     {
-      /* Remove course point */
-      GPS_Course_Point_Del(&cpt[i]);
-      memmove(&cpt[i], &cpt[i+1], (*n_cpt-i-1)*sizeof(*cpt));
-      (*n_cpt)--;
-      goto restart_course_points;
+        /* Check for unreferenced point */
+        for (j=0; j<*n_crs; j++)
+            if (crs[j]->index == cpt[i]->course_index)
+                break;
+        if (j<*n_crs)
+        {
+            /* Check for duplicate point */
+            for (j=0; j<i; j++)
+                if (cpt[i]->course_index == cpt[j]->course_index &&
+                    cpt[i]->track_point_time == cpt[j]->track_point_time)
+                    break;
+            if (j>=i)
+                continue; /* Referenced & unique */
+        }
+        /* Remove course point */
+        GPS_Course_Point_Del(&cpt[i]);
+        memmove(&cpt[i], &cpt[i+1], (*n_cpt-i-1)*sizeof(*cpt));
+        (*n_cpt)--;
+        goto restart_course_points;
     }
-  }
 }
 
 
@@ -995,19 +1132,22 @@ restart_course_points:
 **
 ** @param [r] port [const char *] serial port
 ** @param [r] trk [GPS_PTrack *] track array
-** @param [r] n [int32] number of track entries
+** @param [r] n_trk [int32] number of track entries
+** @param [r] wpt [GPS_PWay *] waypoint array
+** @param [r] n_wpt [int32] number of waypoint entries
 **
 ** @return [int32] success
 ************************************************************************/
 
-int32 GPS_Command_Send_Track_As_Course(const char *port, GPS_PTrack *trk, int32 n)
+int32 GPS_Command_Send_Track_As_Course(const char *port, GPS_PTrack *trk, int32 n_trk,
+                                       GPS_PWay *wpt, int32 n_wpt)
 {
     GPS_PCourse *crs = NULL;
     GPS_PCourse_Lap *clp = NULL;
     GPS_PTrack *ctk = NULL;
     GPS_PCourse_Point *cpt = NULL;
     int n_crs, n_clp=0, n_ctk=0, n_cpt=0;
-    int i, trk_end, trk_crs;
+    int i, j, trk_end, new_crs, first_new_ctk;
     int32 ret;
 
     /* Read existing courses from device */
@@ -1015,80 +1155,97 @@ int32 GPS_Command_Send_Track_As_Course(const char *port, GPS_PTrack *trk, int32
     if (n_crs < 0) return n_crs;
 
     /* Create new course+lap+track points for each track */
-    trk_crs = n_crs;
-    for (i=0;i<n;i++) {
-       if (!trk[i]->ishdr)
-           continue;
-
-       /* Find end of track */
-       for (trk_end=i; trk_end<n-1; trk_end++)
-           if (trk[trk_end+1]->ishdr)
-               break;
-       if (trk_end==i)
-           trk_end=0; /* Empty track */
-
-       /* Create & append course */
-       crs = realloc(crs, (n_crs+1) * sizeof(GPS_PCourse));
-       if (!crs) return MEMORY_ERROR;
-       crs[n_crs] = GPS_Course_New();
-       if (!crs[n_crs]) return MEMORY_ERROR;
-
-       crs[n_crs]->index = Unique_Course_Index(crs, n_crs);
-       strncpy(crs[n_crs]->course_name, trk[i]->trk_ident,
-               sizeof(crs[n_crs]->course_name)-1);
-
-       crs[n_crs]->track_index = Unique_Track_Index(crs, n_crs);
-
-       /* Create & append new lap */
-       clp = realloc(clp, (n_clp+1) * sizeof(GPS_PCourse_Lap));
-       if (!clp) return MEMORY_ERROR;
-       clp[n_clp] = GPS_Course_Lap_New();
-       if (!clp[n_clp]) return MEMORY_ERROR;
-
-       clp[n_clp]->course_index = crs[n_crs]->index; /* Index of associated course */
-       clp[n_clp]->lap_index = 0; /* Lap index, unique per course */
-       clp[n_clp]->total_time = 0; /* FIXME: Calculate */
-       clp[n_clp]->total_dist = 0; /* FIXME: Calculate */
-       if (trk_end)
-       {
-           clp[n_clp]->begin_lat = trk[i+1]->lat;
-           clp[n_clp]->begin_lon = trk[i+1]->lon;
-           clp[n_clp]->end_lat = trk[trk_end]->lat;
-           clp[n_clp]->end_lon = trk[trk_end]->lon;
-       }
-       else
-       {
-           clp[n_clp]->begin_lat = 0x7fffffff;
-           clp[n_clp]->begin_lon = 0x7fffffff;
-           clp[n_clp]->end_lat = 0x7fffffff;
-           clp[n_clp]->end_lon = 0x7fffffff;
-       }
-       clp[n_clp]->avg_heart_rate = 0; /* FIXME: Calculate */
-       clp[n_clp]->max_heart_rate = 0; /* FIXME: Calculate */
-       clp[n_clp]->intensity = 0;
-       clp[n_clp]->avg_cadence = 0xff; /* FIXME: Calculate */
-       n_crs++;
-       n_clp++;
+    new_crs = n_crs;
+    for (i=0;i<n_trk;i++) {
+        if (!trk[i]->ishdr)
+            continue;
+
+        /* Find end of track */
+       for (trk_end=i; trk_end<n_trk-1; trk_end++)
+            if (trk[trk_end+1]->ishdr)
+                break;
+        if (trk_end==i)
+            trk_end=0; /* Empty track */
+
+        /* Create & append course */
+        crs = xrealloc(crs, (n_crs+1) * sizeof(GPS_PCourse));
+        crs[n_crs] = GPS_Course_New();
+        if (!crs[n_crs]) return MEMORY_ERROR;
+
+        crs[n_crs]->index = Unique_Course_Index(crs, n_crs);
+        strncpy(crs[n_crs]->course_name, trk[i]->trk_ident,
+                sizeof(crs[n_crs]->course_name)-1);
+
+        crs[n_crs]->track_index = Unique_Track_Index(crs, n_crs);
+
+        /* Create & append new lap */
+        clp = xrealloc(clp, (n_clp+1) * sizeof(GPS_PCourse_Lap));
+        clp[n_clp] = GPS_Course_Lap_New();
+        if (!clp[n_clp]) return MEMORY_ERROR;
+
+        clp[n_clp]->course_index = crs[n_crs]->index; /* Index of associated course */
+        clp[n_clp]->lap_index = 0; /* Lap index, unique per course */
+       Calculate_Course_Lap_Data(clp[n_clp], trk, i+1, trk_end);
+        n_crs++;
+        n_clp++;
     }
 
     /* Append new track points */
-    ctk = realloc(ctk, (n_ctk+n) * sizeof(GPS_PTrack));
-    if (!ctk) return MEMORY_ERROR;
-
-    for (i=0;i<n;i++) {
-       ctk[n_ctk] = GPS_Track_New();
-       if (!ctk[n_ctk]) return MEMORY_ERROR;
-       *ctk[n_ctk] = *trk[i];
-
-       if (ctk[n_ctk]->ishdr)
-       {
-           /* Index of new track, must match the track index in associated course */
-           memset(ctk[n_ctk]->trk_ident, 0, sizeof(ctk[n_ctk]->trk_ident));
-           sprintf(ctk[n_ctk]->trk_ident, "%d", crs[trk_crs]->track_index);
-           trk_crs++;
-       }
-       n_ctk++;
+    ctk = xrealloc(ctk, (n_ctk+n_trk) * sizeof(GPS_PTrack));
+
+    first_new_ctk = n_ctk;
+    for (i=0;i<n_trk;i++) {
+        ctk[n_ctk] = GPS_Track_New();
+        if (!ctk[n_ctk]) return MEMORY_ERROR;
+        *ctk[n_ctk] = *trk[i];
+
+        if (ctk[n_ctk]->ishdr)
+        {
+            /* Index of new track, must match the track index in associated course */
+            memset(ctk[n_ctk]->trk_ident, 0, sizeof(ctk[n_ctk]->trk_ident));
+            sprintf(ctk[n_ctk]->trk_ident, "%d", crs[new_crs]->track_index);
+            new_crs++;
+        }
+        n_ctk++;
+    }
+
+    /* Convert waypoints to course points by searching closest track point &
+     * append
+     */
+    cpt = xrealloc(cpt, (n_cpt+n_wpt) * sizeof(GPS_PCourse_Point));
+    for (i=0; i<n_wpt; i++)
+    {
+       double dist, min_dist = DBL_MAX;
+       int min_dist_idx = 0, trk_idx = 0, min_dist_trk_idx = 0;
+
+       /* Find closest track point */
+       for (j=first_new_ctk; j<first_new_ctk+n_trk; j++) {
+           if (ctk[j]->ishdr) {
+               trk_idx = strtoul(ctk[j]->trk_ident, NULL, 0);
+               continue;
+           }
+
+           dist = gcgeodist(wpt[i]->lat, wpt[i]->lon, ctk[j]->lat, ctk[j]->lon);
+           if (dist < min_dist) {
+               min_dist = dist;
+               min_dist_idx = j;
+               min_dist_trk_idx = trk_idx;
+           }
+       }
+
+       cpt[i+n_cpt] = GPS_Course_Point_New();
+       strncpy(cpt[i+n_cpt]->name, wpt[i]->cmnt,
+               sizeof(cpt[i+n_cpt]->name) - 1);
+       for (j=0; j<n_crs; j++)
+           if (crs[j]->track_index == min_dist_trk_idx)
+           {
+               cpt[i+n_cpt]->course_index = crs[j]->index;
+               break;
+           }
+       cpt[i+n_cpt]->track_point_time = ctk[min_dist_idx]->Time;
+       cpt[i+n_cpt]->point_type = 0;
     }
+    n_cpt += n_wpt;
 
     /* Remove course data that's no longer needed */
     Course_Garbage_Collect(crs, &n_crs, clp, &n_clp, ctk, &n_ctk, cpt, &n_cpt);
index d0d5b7f49fe466efedbc9f3af9131f27596c7cb8..e55fe894303888eae01da6c3eda180f99249505c 100644 (file)
@@ -42,7 +42,8 @@ int32  GPS_Command_Get_Lap(const char *port, GPS_PLap **lap, int (*cb)(int, stru
 int32  GPS_Command_Send_Course(const char *port, GPS_PCourse *crs, GPS_PCourse_Lap *clp,
                                GPS_PTrack *trk, GPS_PCourse_Point *cpt,
                                int32 n_crs, int32 n_clp, int32 n_trk, int32 n_cpt);
-int32  GPS_Command_Send_Track_As_Course(const char *port, GPS_PTrack *trk, int32 n);
+int32  GPS_Command_Send_Track_As_Course(const char *port, GPS_PTrack *trk, int32 n_trk,
+                                        GPS_PWay *wpt, int32 n_wpt);
 
 int32  GPS_Command_Get_Workout(const char *port, void **lap, int (*cb)(int, struct GPS_SWay **));
 int32  GPS_Command_Get_Fitness_User_Profile(const char *port, void **lap, int (*cb)(int, struct GPS_SWay **));